package msu.ml.data.level2;

import java.io.*;

public class Level2InputStream
{
   private InputStream is;

   public Level2InputStream(InputStream is)
   {
      this.is = is;
   }

	/**
	 * Read the specified number of bytes into the specified
	 * buffer starting at the beginning of the buffer.
	 *
	 * @param buffer the buffer to read into
	 * @param length the number of bytes to read
	 */
	public void readBytes(byte[] buffer, int length) 
		throws IOException
	{
		readBytes(buffer, 0, length);
	}

	/**
	 * Read the specified number of bytes into the specified
	 * buffer starting at the specified offset.
	 *
	 * @param buffer the buffer to read into
	 * @param offset the offset in the buffer to begin at
	 * @param length the number of bytes to read
	 */
	public void readBytes(byte[] buffer, int offset, int length) 
		throws IOException
	{
      try
      {
         int sum = 0;
         int n = this.is.read(buffer, offset, length);
         if(n == -1)
            throw new Exception();

         sum += n;
         while(sum < length)
         {
            n = this.is.read(buffer, offset + sum, length - sum);
            if(n == -1)
               throw new Exception();
            sum += n;
         }
      }
      catch(Exception e)
      {
         System.out.println("Buffer Length: " + length);
         e.printStackTrace();
         System.exit(0);
      }
	}

   public void readBytes(char[] buffer, int offset, int length)
      throws IOException
   {
      byte [] bytes = new byte[buffer.length];
      readBytes(bytes, offset, length);
      for(int i = 0; i < bytes.length; i++)
         buffer[i] = (char)bytes[i];
   }

	/**
	 * Read a int from the base input stream
	 *
	 * @return the next int of data
	 */
	public int readInt() throws IOException
	{
		byte[] value = new byte[4];
		readBytes(value, 4);
		return (0x000000FF & ((int)value[0])) << 24 |
			(0x000000FF & ((int)value[1])) << 16 |
			(0x000000FF & ((int)value[2])) << 8 |
			(0x000000FF & ((int)value[3]));
	}

	/**
	 * Read a float from the base input stream
	 *
	 * @return the next float of data
	 */
	public float readFloat() throws IOException
	{
		return Float.intBitsToFloat(readInt());
	}

	/**
	 * Read a short from the base input stream
	 *
	 * @return the next short of data
	 */
	public short readShort() throws IOException
	{
		byte[] value = new byte[2];
		readBytes(value, 2);
		return (short)((0x000000FF & ((int)value[0])) << 8 |
				(0x000000FF & ((int)value[1])));
	}

   public byte readByte() throws IOException
   {
      byte[] value = new byte[1];
      readBytes(value, 1);
      return value[0];
   }

   public long skip(int n) throws IOException
   {
      return this.is.skip(n);
   }

	/**
	 * Close the data stream
	 */
	public void close()
	{
		try
		{
			is.close();
		}
		catch(IOException e)
		{
         System.out.println("Level2InputStream Error: " + e.getMessage());
         e.printStackTrace();
		}
	}

	/**
	 * Close the data stream
	 */
	protected void finalize() throws Throwable 
	{
		try 
		{
			close();        // close open files
		} 
		finally 
		{
			super.finalize();
		}
	}
}
